// 字符字面量、字符组、量词、锚、分组、选择分支、反向引用
// 结构     说明

// 字面量   匹配一个具体字符，包括不用转义的和需要转义的。比如 a 匹配字符 "a"，
// 又比如   \n 匹配换行符，又比如 \. 匹配小数点。

// 字符组
// 匹配一个字符，可以是多种可能之一，比如 [0-9]，表示匹配一个数字。
// 也有 \d 的简写形式。
// 另外还有反义字符组，表示可以是除了特定字符之外任何一个字符，比如 [^0-9]，
// 表示一个非数字字符，也有 \D 的简写形式。


// 量词
// 表示一个字符连续出现，比如 a{1,3} 表示 "a" 字符连续出现 3 次。
// 另外还有常见的简写形式，比如 a+ 表示 "a" 字符连续出现至少一次。


// 锚
// 匹配一个位置，而不是字符。比如 ^ 匹配字符串的开头，又比如 \b 匹配单词边界，
// 又比如 (?=\d) 表示数字前面的位置。


// 分组
// 用 也可以使用 括号表示一个 非捕整 获分 体，
// 比如 组 (?:ab)+ (ab)+ 。，表示 "ab" 两个字符连续出现多次，


// 分支 多个子表达式多选一，
// 比如 abc|bcd，表达式匹配 "abc" 或者 "bcd" 字符子串。
// 反向引用，比如 \2，表示引用第 2 个分组。



//        操作符描述 操作符 
// 优先级1 转义符     \ 
// 优先级2 括号和方括号  (…)、(?:…)、(?=…)、(?!…)、[…] 
// 优先级3 量词限定符  {m}、{m,n}、{m,}、?、*、+ 
// 优先级4 位置和序列  ^、$、\元字符、一般字符 
// 优先级5 管道符（竖杠） | 

// 元字符转义问题
; (function () {
    //return
    // ^、$、.、*、+、?、|、\、/、(、)、[、]、{、}、=、!、:、- ,
    // 当匹配上面的字符本身时，可以一律转义：
    var string = "^$.*+?|\\/[]{}=!:-,";
    var regex = /\^\$\.\*\+\?\|\\\/\[\]\{\}\=\!\:\-\,/;
    console.log(regex.test(string));
    // => true

    // 另外，在 string 中，也可以把每个字符转义，当然，转义后的结果仍是本身
    var string3 = "^$.*+?|\\/[]{}=!:-,";
    var string2 = "\^\$\.\*\+\?\|\\\/\[\]\{\}\=\!\:\-\,";
    console.log(string3 == string2);
    // => true


    var string = "^$.*+?|\\/[]{}=!:-,";
    var regex = /[\^$.*+?|\\/\[\]{}=!:\-,]/g;
    console.log(string.match(regex));
    // => ["^", "$", ".", "*", "+", "?", "|", "\", "/", "[", "]", "{", "}", "=", "!", ":",
    // "-", ","]


    // 我们知道[abc]，是个字符组。如果要匹配字符串 "[abc]" 时，该怎么办？
    // 可以写成 /\[abc\] /，也可以写成 /\[abc] /，测试如下
    var string = "[abc]";
    var regex = /\[abc]/g;
    console.log(string.match(regex)[0]);
    // => "[abc]"

    // {}和[]一样, 只需要\[或者\{那么后面的]和}就默认不会发生歧义
    // 比如 =、!、:、-、, 等符号，只要不在特殊结构中，并不需要转义。
    // 但是，括号需要前后都转义的，如 /\(123\)/。
    // 至于剩下的 ^、$、.、*、+、?、|、\、/ 等字符，只要不在字符组内，都需要转义的。
})();

// 现在实践
; (function () {
    return
    let regex = /ab?(c|de*)+|fg/
    // 由于括号的存在，所以，(c | de *) 是一个整体结构。
    // 在(c | de *) 中，注意其中的量词 ，因此 e 是一个整体结构。
    // 又因为分支结构 | 优先级最低，因此 c 是一个整体、而 de * 是另一个整体。
    // 同理，整个正则分成了 a、b ?、(…) +、f、g。而由于分支的原因，
    // 又可以分成 ab ? (c | de *) + 和 fg 这两部分。
    let str = 'abdec'
    console.log(regex.exec(str));
})();

// 实践2
; (function () {
    return
    let regex = /^abc|ccc$/
    let str = 'abdcccec'
    let str1 = 'abcdec'
    let str2 = 'abdeccc'
    console.log(regex.test(str)); // false
    console.log(regex.test(str1)); // true
    console.log(regex.test(str2)); //true
})();

//实践身份证
; (function () {
    //return
    /^(\d{15}|\d{17}[\dxX])$/
    // 因为竖杠 | 的优先级最低，所以正则分成了两部分 \d{ 15 } 和 \d{ 17 } [\dxX]。
    // \d{ 15 } 表示 15 位连续数字。
    // \d{ 17 } [\dxX] 表示 17 位连续数字，最后一位可以是数字，可以大小写字母 "x"。

})();

// 实践ipv4
; (function () {
    //return
    // 正则表达式是：
    /^((0{0,2}\d|0?\d{2}|1\d{2}|2[0-4]\d|25[0-5])\.){3}(0{0,2}\d|0?\d{2}|1\d{2}|2[0-4]\d|25[0-5])$/
    // 这个正则，看起来非常吓人。但是熟悉优先级后，会立马得出如下的结构：
    // 第一段==> (\.){3}
    // 第二段==> (0{0,2}\d|0?\d{2}|1\d{2}|2[0-4]\d|25[0-5])

    // 第一段分解为
    // (0{0,2}\d|0?\d{2}|1\d{2}|2[0-4]\d|25[0-5]) 和 \.
    // 0{0,2}\d  0?\d{2} 1\d{2}         2[0-4]\d  25[0-5]
    // 个位数    双位数  三位数100-199   200-249   250-255

    
    // 其中，两个(…) 是一样的结构。表示匹配的是 3 位数字。因此整个结构是
    // 3位数.3位数.3位数.3位数
    // 然后再来分析(…)：
    // (0{ 0, 2 } \d | 0 ?\d{ 2 }| 1\d{ 2 }| 2[0 - 4]\d | 25[0 - 5])
    // 它是一个多选结构，分成5个部分：
    // 0{ 0, 2 } \d，匹配一位数，包括 "0" 补齐的。比如，"9"、"09"、"009"；
    // 0 ?\d{ 2 }，匹配两位数，包括 "0" 补齐的，也包括一位数；
    // 1\d{ 2 }，匹配 "100" 到 "199";
    // 2[0 - 4]\d，匹配 "200" 到 "249"；
    // 25[0 - 5]，匹配 "250" 到 "255"。
})();